﻿using System;
using JpLabs.CustomEvents;

namespace Duellum.Core
{
	public class ValueChangedEventArgs<T> : EventArgs
	{
		public ValueChangedEventArgs(T oldValue, T newValue)
		{
			OldValue = oldValue;
			NewValue = newValue;
		}
		
		public T NewValue { get; private set; }
		public T OldValue { get; private set; }
		
		private bool? didValueChanged;
		public bool DidValueChanged
		{
			get {
				if (didValueChanged == null) didValueChanged = object.Equals(OldValue, NewValue);
				return didValueChanged.Value;
			}
		}
	}

	public class TrackableValue<T> //where T : struct
	{
		private CustomEvent<EventHandler<ValueChangedEventArgs<T>>> valueChangedEvent;
		
		private T innerValue;

		public TrackableValue()
			: this(default(T)) {}

		public TrackableValue(object owner)
			: this(owner, default(T)) {}

		private TrackableValue(object owner, T value)
		{
			Owner = owner ?? this;
			innerValue = value;
			valueChangedEvent = CreateEventHolder();
		}
		
		//private TrackableValue(object owner, T value, CustomEvent<EventHandler<ValueChangedEventArgs<T>>> customEvent)
		//{
		//    Owner = owner ?? this;
		//    innerValue = value;
		//    valueChangedEvent = customEvent;
		//}
		
		protected virtual CustomEvent<EventHandler<ValueChangedEventArgs<T>>> CreateEventHolder()
		{
			//return new FastSmartWeakEvent<EventHandler<ValueChangedEventArgs<T>>>();
			//return new WeakSyncedEvent<EventHandler<ValueChangedEventArgs<T>>>();
			return new WeakEvent<EventHandler<ValueChangedEventArgs<T>>>();
		}

		static public implicit operator T(TrackableValue<T> value)
		{
			return value.Value;
		}

		public T Value
		{
			get { return innerValue; }
			set {
				var oldValue = innerValue;
				innerValue = value;
				OnValueChanged(oldValue, innerValue);
			}
		}

		public event EventHandler<ValueChangedEventArgs<T>> ValueChanged
		{
			add    { valueChangedEvent += value; }
			remove { valueChangedEvent -= value; }
		}
		
		/// <summary>
		/// This property is only used to set the 'sender' parameter to the event handler
		/// </summary>
		public object Owner { get; private set; }
		
		private void OnValueChanged(T oldValue, T newValue)
		{
			valueChangedEvent.Raise(Owner, new ValueChangedEventArgs<T>(oldValue, newValue));
		}
	}
}
